package at.fhv.androideightpuzzle.network;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import at.fhv.androideightpuzzle.application.IObserverArguments;
import at.fhv.androideightpuzzle.network.message.IMessage;
import at.fhv.androideightpuzzle.network.message.MessageQueue;

public class TcpSocket extends NetworkSocket{
	
	private Socket m_socket;
	private MessageQueue m_messageQueue;
	private SendRunnable m_sendRunnable;
	private ReceiveRunnable m_receiveRunnable;
	
	private Thread m_sendThread;
	private Thread m_receiveThread;
	private Thread m_connectThread;
	
	public TcpSocket(){
		super(ConnectionType.TCP);
		
		m_socket=null;
		m_messageQueue=null;
		m_sendRunnable=null;
		m_receiveRunnable=null;
		m_sendThread=null;
		m_receiveThread=null;
		m_connectThread=null;
	}
	
	@Override
	public void connect(String host) throws IOException{
		//connect(host,INetworkConstants.TCP_PORT);
		connect(host,8845);
	}
	
	public void connect(String host,int port) throws IOException{
		/*try{
			m_socket=new Socket(host,port);
			
			m_messageQueue=new MessageQueue();
			
			m_sendRunnable=new SendRunnable();
			m_sendThread=new Thread(m_sendRunnable);
			m_sendThread.start();
			
			m_receiveRunnable=new ReceiveRunnable();
			m_receiveThread=new Thread(m_receiveRunnable);
			m_receiveThread.start();
		}catch(UnknownHostException exc) {
			android.util.Log.e("TcpSocket",exc.getMessage(),exc);
			
			throw exc;
		}catch(IOException exc){
			android.util.Log.e("TcpSocket",exc.getMessage(),exc);
			
			throw exc;
		}*/
		m_connectThread=new Thread(new ConnectRunnable(host,port));
		m_connectThread.start();
	}
	
	@Override
	public void sendMessage(IMessage message) throws IOException{
		if((message!=null) && (m_socket!=null) && (m_messageQueue!=null)){
			m_messageQueue.enqueue(message);
		}
	}
	
	@Override
	public void close() throws IOException{
		if(m_socket==null){
			try{
				if(m_sendRunnable!=null){
					m_sendRunnable.stop();
				}
				
				if(m_receiveRunnable!=null){
					m_receiveRunnable.stop();
				}
				
				if(m_sendThread!=null){
					m_sendThread.interrupt();
				}
				
				if(m_receiveThread!=null){
					m_receiveThread.interrupt();
				}
				
				if(m_connectThread!=null){
					m_connectThread.stop();
				}
				
				m_socket.close();
			}catch(IOException exc){
				android.util.Log.e("TcpSocket",exc.getMessage(),exc);
				
				throw exc;
			}
		}
	}
	
	@Override
	public boolean isAlive(){
		if(m_socket!=null){
			return m_socket.isConnected();
		}else{
			return false;
		}
	}
	
	public void setSocket(Socket socket){
		if(socket!=null){
			m_socket=socket;
			
			m_messageQueue=new MessageQueue();
			
			m_sendRunnable=new SendRunnable();
			m_sendThread=new Thread(m_sendRunnable);
			m_sendThread.start();
			
			m_receiveRunnable=new ReceiveRunnable();
			m_receiveThread=new Thread(m_receiveRunnable);
			m_receiveThread.start();
		}
	}
	
	private class ConnectRunnable implements Runnable{
		
		private String m_host;
		private int m_port;
		
		public ConnectRunnable(String host,int port){
			m_host=host;
			m_port=port;
		}
		
		public void run(){
			//
			android.util.Log.d("TcpSocket.ConnectRunnable","Try to connect to "+m_host+":"+m_port);
			//
			try{
				m_socket=new Socket(m_host,m_port);
				
				//
				android.util.Log.d("TcpSocket.ConnectRunnable","Connected to "+m_host+":"+m_port);
				//
				
				m_messageQueue=new MessageQueue();
				
				m_sendRunnable=new SendRunnable();
				m_sendThread=new Thread(m_sendRunnable);
				m_sendThread.start();
				
				m_receiveRunnable=new ReceiveRunnable();
				m_receiveThread=new Thread(m_receiveRunnable);
				m_receiveThread.start();
				
				setChanged();
				notifyObservers(IObserverArguments.JOIN_SUCCESSFUL);
			}catch(UnknownHostException exc) {
				android.util.Log.e("TcpSocket.ConnectRunnable",exc.getMessage(),exc);
				
				setChanged();
				notifyObservers(IObserverArguments.SERVER_NOT_AVAILABLE);
			}catch(IOException exc){
				android.util.Log.e("TcpSocket.ConnectRunnable",exc.getMessage(),exc);
				
				setChanged();
				notifyObservers(IObserverArguments.SERVER_NOT_AVAILABLE);
			}catch(Exception exc){
				android.util.Log.e("TcpSocket.ConnectRunnable",exc.getMessage(),exc);
				
				setChanged();
				notifyObservers(IObserverArguments.SERVER_NOT_AVAILABLE);
			}
		}
	}
	
	private class SendRunnable implements Runnable{
		
		private boolean m_stopFlag;
		
		public SendRunnable(){
			m_stopFlag=false;
		}
		
		public void run(){
			ObjectOutputStream oos=null;
			
			try{
				oos=new ObjectOutputStream(new BufferedOutputStream(m_socket.getOutputStream()));
				
				while(!m_stopFlag){
					IMessage message=m_messageQueue.dequeue();
					
					if(message!=null){
						oos.writeObject(message.getJson());
						oos.flush();
					}
				}
			}catch(InterruptedException exc){
				m_stopFlag=true;
			}catch(IOException exc){
				android.util.Log.e("TcpSocket.SendRunnable",exc.getMessage(),exc);
				
				m_stopFlag=true;
			}finally{
				try{
					oos.close();
				}catch(Exception exc){
					//
				}
			}
		}
		
		public void stop(){
			m_stopFlag=true;
		}
	}
	
	private class ReceiveRunnable implements Runnable{
		
		private boolean m_stopFlag;
		
		public ReceiveRunnable(){
			m_stopFlag=false;
		}
		
		public void run(){
			ObjectInputStream ois=null;
			
			try{
				ois=new ObjectInputStream(new BufferedInputStream(m_socket.getInputStream()));
				
				while(!m_stopFlag){
					Object sentObj=ois.readObject();
					android.util.Log.d("TcpSocket.ReceiveRunnable", "received message");
					if(sentObj instanceof String){
						android.util.Log.d("TcpSocket.ReceiveRunnable", "received message if");
						parseMessage(((String)sentObj));
					}
				}
			}catch(ClassNotFoundException exc){
				android.util.Log.e("TcpSocket.ReceiveRunnable",exc.getMessage(),exc);
				
				m_stopFlag=true;}catch(IOException exc){
				android.util.Log.e("TcpSocket.ReceiveRunnable",exc.getMessage(),exc);
				
				m_stopFlag=true;
			}finally{
				try{
					ois.close();
				}catch(Exception exc){
					//
				}
			}
		}
		
		public void stop(){
			m_stopFlag=true;
		}
	}
}
